package com.shuai.frame.spi;

import cn.hutool.core.io.resource.ResourceUtil;
import com.shuai.frame.fault.retry.RetryStrategy;
import com.shuai.frame.loadbalancer.LoadBalancer;
import com.shuai.frame.registry.RegistryCenter;
import com.shuai.frame.serializer.Serializer;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class XSRpcSPILoader {
    /**
     * 缓存加载过的SPI实现
     * key；SPI接口名
     * value: key：SPI接口名对应的实现类的key;value：SPI接口名对应的实现类
     */
    private static final Map<String, Map<String, Class<?>>> SPA_MAP_CACHE = new ConcurrentHashMap<>();

    /**
     * SPI系统配置目录
     */
    private static final String XS_RPC_SYSTEM_DIR = "META-INF/xs-rpc/system/";

    /**
     * SPI用户自定义配置目录
     */
    private static final String XS_RPC_CUSTOM_DIR = "META-INF/xs-rpc/custom/";

    /**
     * SPI规范接口列表，指定加载这些接口类的实现
     */
    private static final List<Class<?>> LOAD_CLASS_LIST = Arrays.asList(Serializer.class,
            RegistryCenter.class,
            RetryStrategy.class,
            LoadBalancer.class);


    /**
     * SPI加载时扫描的路径
     */
    private static final List<String> SCAN_DIR = Arrays.asList(XS_RPC_SYSTEM_DIR, XS_RPC_CUSTOM_DIR);


    /**
     * 存放创建好的额SPI实现
     */
    private static final Map<String, Object> SPI_IMPL_MAP = new ConcurrentHashMap<>();

    /**
     * 加载所有的SPI接口实现
     */
    public static void loadSPIImpl() {
        if (!SPA_MAP_CACHE.isEmpty()) {
            // 已加载过SPI，取消加载
            return;
        }
        for (Class<?> aClass : LOAD_CLASS_LIST) {
            load(aClass);
        }
    }


    /**
     * 加载接口 spiItr的接口实现
     *
     * @param spiItr 要加载的SPI接口
     */
    private static void load(Class<?> spiItr) {
        String itrName = spiItr.getName();
        log.info("加载SPI--【{}】的接口实现开始\n", itrName);
        Map<String, Class<?>> classMap = new HashMap<>();
        for (String spiLoadDir : SCAN_DIR) {
            List<URL> urlResources = ResourceUtil.getResources(spiLoadDir + itrName);
            if (urlResources.isEmpty() && !XS_RPC_CUSTOM_DIR.equals(spiLoadDir)) {
                log.error("未从classpath:【{}】下找到SPI--【{}】的实现\n", spiLoadDir, itrName);
                throw new RuntimeException("未从classpath:【" + spiLoadDir + "】下找到SPI--【" + itrName + "】的实现");
            }
            for (URL urlResource : urlResources) {
                try (InputStreamReader inputStreamReader = new InputStreamReader(urlResource.openStream()); BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
                    String line;
                    while ((line = bufferedReader.readLine()) != null) {
                        verifyLegality(line);
                        String[] split = line.split("=");
                        classMap.put(split[0], Class.forName(split[1]));
                    }
                } catch (IOException e) {
                    log.error("IO阻塞异常，读取SPI--【{}】实现文档失败\n", spiItr);
                    throw new RuntimeException(e);
                } catch (ClassNotFoundException e) {
                    log.error("SPI--【{}】文档中配置的实现类不存在，异常信息：{}\n", spiItr, e.getMessage());
                    throw new RuntimeException(e);
                }
            }
        }
        SPA_MAP_CACHE.put(itrName, classMap);
        log.info("加载SPI--【{}】的接口实现完成\n", itrName);
    }


    /**
     * @param line SPI 文件的每一行
     * @TODO: 优化line的校验逻辑   2024年6月2日21:04:47
     */
    private static void verifyLegality(String line) {
        if (line == null || !line.contains("=")) {
            throw new RuntimeException("SPI文件配置错误");
        }
        String[] parts = line.split("=");
        if (parts.length != 2) {
            throw new RuntimeException("SPI文件配置错误");
        }
        if (parts[0].isEmpty() || parts[1].isEmpty()) {
            throw new RuntimeException("SPI文件配置错误");
        }
    }


    /**
     * 返回SPI的指定实现
     */
    public static <T> T getInstance(Class<?> spiItrClass, String key) throws InstantiationException, IllegalAccessException {
        String spiItrName = spiItrClass.getName();
        Map<String, Class<?>> classMap = SPA_MAP_CACHE.get(spiItrName);
        if (classMap == null) {
            log.error("SPI--【{}】相关属性未加载\n", spiItrName);
            throw new RuntimeException("SPI--【" + spiItrName + "】相关属性未加载");
        }
        if (!classMap.containsKey(key)) {
            log.error("SPI--【{}】的实现中未找到key为【{}】对应的实现类\n", spiItrName, key);
        }
        Class<?> impl = classMap.get(key);
        String implName = impl.getName();
        if (!SPI_IMPL_MAP.containsKey(implName)) {
            SPI_IMPL_MAP.put(implName, impl.newInstance());
        }
        return (T) SPI_IMPL_MAP.get(implName);
    }
}
